package stack

import "unsafe"

type Stack struct {
	//The size of the Stack(unit: byte).
	Size  int
	Space []byte
	//Stack pointer.
	Sp uintptr
	//Base pointer.
	Bp       uintptr
	maxPoint uintptr
}

func Push[T interface{}](s *Stack, v T) {
	size := unsafe.Sizeof(v)
	if s.Sp+size > s.maxPoint {
		panic(ErrStackOverflow{})
	}
	*(*T)(unsafe.Pointer(s.Sp)) = v
	s.Sp += size
}

func Pop[T interface{}](s *Stack) T {
	var v T
	size := unsafe.Sizeof(v)
	if s.Sp-size < s.Bp {
		panic(ErrPopEmptyStack{})
	}
	v = GetValueByPointerAndOffset[T](s.Sp, -size)
	s.Sp -= size
	return v
}

func PopStackFrame(s *Stack) {
	if s.Bp == 0 {
		panic(ErrPopBaseStackFrame{})
	}
	p := GetAt[uintptr](s.Bp)
	s.Sp = s.Bp
	s.Bp = p
}

func PushStackFrame(s *Stack) {
	b := s.Sp
	Push[uintptr](s, s.Bp)
	s.Bp = b
}
